#include "lfs_fun.h"
#include "w25qxx.h"

#define __DEBUG__ 	1
#if __DEBUG__  
#include "uart.h"
    #define DEBUG(format,...)    printf(""format"", ##__VA_ARGS__)  

#else
    #define DEBUG(format,...)  
#endif

// variables used by the filesystem
lfs_t lfs;
lfs_file_t file;

static int _block_read(const struct lfs_config *c, lfs_block_t block, lfs_off_t off, void *buffer, lfs_size_t size) {
	W25QXX_Read(block * c->block_size + off, (uint8_t*)buffer, size);
    return 0;
}
static int _block_prog(const struct lfs_config *c, lfs_block_t block, lfs_off_t off, const void *buffer, lfs_size_t size) {
	W25QXX_Write(block * c->block_size + off, (uint8_t*)buffer, size);
    return 0;
}
static int _block_erase(const struct lfs_config *c, lfs_block_t block) {
	W25QXX_EraseSector(block * c->block_size);
    return 0;
}
static int _block_sync(const struct lfs_config *c) {
    return 0;
}

// configuration of the filesystem is provided by this struct
const struct lfs_config cfg = {
    // block device operations
    .read  = _block_read,
    .prog  = _block_prog,
    .erase = _block_erase,
    .sync  = _block_sync,

    // block device configuration
    .read_size = 128,
    .prog_size = 128,
    .block_size = 4096,
    .block_count = 4096,
	.block_cycles = 200,
	.cache_size = 4096,
    .lookahead_size = 128,
};

static void LFS_PrintErr(int err) {
	switch(err) {
		case LFS_ERR_OK:
			printf("LFS: err = LFS_ERR_OK\n");
		break;
		case LFS_ERR_IO:
			printf("LFS: err = LFS_ERR_IO\n");
		break;
		case LFS_ERR_CORRUPT:
			printf("LFS: err = LFS_ERR_CORRUPT\n");
		break;
		case LFS_ERR_NOENT:
			printf("LFS: err = LFS_ERR_NOENT\n");
		break;
		case LFS_ERR_EXIST:
			printf("LFS: err = LFS_ERR_EXIST\n");
		break;
		case LFS_ERR_NOTDIR:
			printf("LFS: err = LFS_ERR_NOTDIR\n");
		break;
		case LFS_ERR_ISDIR:
			printf("LFS: err = LFS_ERR_ISDIR\n");
		break;
		case LFS_ERR_NOTEMPTY:
			printf("LFS: err = LFS_ERR_NOTEMPTY\n");
		break;
		case LFS_ERR_BADF:
			printf("LFS: err = LFS_ERR_BADF\n");
		break;
		case LFS_ERR_FBIG:
			printf("LFS: err = LFS_ERR_FBIG\n");
		break;
		case LFS_ERR_INVAL:
			printf("LFS: err = LFS_ERR_INVAL\n");
		break;
		case LFS_ERR_NOSPC:
			printf("LFS: err = LFS_ERR_NOSPC\n");
		break;
		case LFS_ERR_NOMEM:
			printf("LFS: err = LFS_ERR_NOMEM\n");
		break;
		case LFS_ERR_NOATTR:
			printf("LFS: err = LFS_ERR_NOATTR\n");
		break;
		case LFS_ERR_NAMETOOLONG:
			printf("LFS: err = LFS_ERR_NAMETOOLONG\n");
		break;
		default:
			printf("LFS: err = UNKNOWN ERR\n");
		break;
	}
}

void LFS_Init(void) {
	int err = LFS_ERR_OK;
	DEBUG("LFS_Init\n");
	err = lfs_mount(&lfs, &cfg);
	if (err != LFS_ERR_OK) {
		LFS_PrintErr(err);
        lfs_format(&lfs, &cfg);
        lfs_mount(&lfs, &cfg);
    }
	uint32_t boot_count = 0;
	lfs_file_open(&lfs, &file, "boot_count", LFS_O_RDWR | LFS_O_CREAT);
    lfs_file_read(&lfs, &file, &boot_count, sizeof(boot_count));
	
	boot_count += 1;
    lfs_file_rewind(&lfs, &file);
    lfs_file_write(&lfs, &file, &boot_count, sizeof(boot_count));
	
	lfs_file_close(&lfs, &file);
	lfs_unmount(&lfs);
	printf("boot_count: %d\n", boot_count);

}
